package org.paboo.chart.qrcode;

import com.google.gson.Gson;
import com.google.gson.JsonIOException;
import com.google.gson.JsonSyntaxException;
import com.google.zxing.WriterException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.paboo.Util.ParameterObject;
import org.paboo.Util.ZxingImage;
import org.paboo.Util.ZxingValidation;
import org.paboo.codec.Base64;
import org.paboo.http.BaseServlet;
import org.paboo.http.HttpMethod;

import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;

/**
 * @author Leonard
 */
public class QrServlet extends BaseServlet {

    private static final long serialVersionUID = -3904063115792721985L;
    private final Logger log = LogManager.getLogger(this.getClass());

    /**
     * Processes requests for both HTTP <code>GET</code> and <code>POST</code>
     * methods.
     *
     * @param request  servlet request
     * @param response servlet response
     * @param m        HTTP method
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException      if an I/O error occurs
     */
    public void processRequest(HttpServletRequest request, HttpServletResponse response, HttpMethod m)
            throws ServletException, IOException {
        QREntity p = new QREntity();
        if (m == HttpMethod.GET) {
            String d = getParameterWithDefault(request, "data", ParameterObject.DEFAULT_CONTECT);
            String c = getParameterWithDefault(request, "encoding", StandardCharsets.ISO_8859_1.name());
            if (!ZxingValidation.checkEncoding(c)) {
                String errorMsg = "Date parameter ERROR!";
                log.warn(errorMsg);
                response.setContentType("text/plain");
                response.getOutputStream().print(errorMsg);
                return;
            }
            p.setData(URLDecoder.decode(d, c));
            p.setEncoding(c);

            String ecLevel = getParameterWithDefault(request, "eclevel", "l");
            if (!ZxingValidation.checkEcLevel(ecLevel)) {
                String errorMsg = "Date parameter ERROR!";
                log.warn(errorMsg);
                response.setContentType("text/plain");
                response.getOutputStream().print(errorMsg);
                return;
            }
            p.setEcLevel(ZxingUtil.parseErrorCorrectLevel(ecLevel));

            String margin = getParameterWithDefault(request, "margin", "1");
            if (ZxingValidation.isNum(margin)) {
                p.setMargin(Integer.parseInt(margin));
            } else {
                String errorMsg = "Date parameter ERROR!";
                log.warn(errorMsg);
                response.setContentType("text/plain");
                response.getOutputStream().print(errorMsg);
                return;
            }

            String size = getParameterWithDefault(request, "size", "200x200");
            if (size.contains("x")) {
                String[] sizea = size.split("x");
                if (ZxingValidation.isNum(sizea[0]) && ZxingValidation.isNum(sizea[1])) {
                    p.setWidth(Integer.parseInt(sizea[0]));
                    p.setHeight(Integer.parseInt(sizea[1]));
                } else {
                    String errorMsg = "Date parameter ERROR!";
                    log.warn(errorMsg);
                    response.setContentType("text/plain");
                    response.getOutputStream().print(errorMsg);
                    return;
                }
            } else {
                String errorMsg = "Date parameter ERROR!";
                log.warn(errorMsg);
                response.setContentType("text/plain");
                response.getOutputStream().print(errorMsg);
                return;
            }

            String bgc = getParameterWithDefault(request, "bgcolor", "DEFAULT");
            String fgc = getParameterWithDefault(request, "fgcolor", "DEFAULT");
            p.setBackgroundColor(ZxingValidation.isHex(bgc) ? Integer.valueOf(bgc.substring(2), 16) : ZxingImage.WHITE);
            p.setForegroundColor(ZxingValidation.isHex(fgc) ? Integer.valueOf(fgc.substring(2), 16) : ZxingImage.BLACK);

            String format = getParameterWithDefault(request, "format", "png");
            p.setNeedBase64(false);
            if (format.contains("|")) {
                String[] f = format.split("\\|");
                format = f[0];
                p.setNeedBase64(f[1].equalsIgnoreCase("base64"));
            }
            if (!ZxingValidation.checkFormat(format.toLowerCase())) {
                String errorMsg = "Date parameter ERROR!";
                log.warn(errorMsg);
                response.setContentType("text/plain");
                response.getOutputStream().print(errorMsg);
                return;
            }
            p.setFormat(format.toLowerCase());
            log.info(new Gson().toJson(p));
        } else if (m == HttpMethod.POST) {
            BufferedReader br = new BufferedReader(
                    new InputStreamReader(request.getInputStream(), request.getCharacterEncoding()));
            Gson gson = new Gson();
            try {
                p = gson.fromJson(br, QREntity.class);
            } catch (JsonIOException | JsonSyntaxException ex) {
                String errorMsg = "Date parameter ERROR!";
                log.error(errorMsg);
                response.setContentType("text/plain");
                response.getOutputStream().print(errorMsg);
                return;
            }
            if (p == null) {
                p = new QREntity();
            }
            if (!ParameterObject.hasText(p.getData())) {
                p.setData(ParameterObject.DEFAULT_CONTECT);
            }
            if (!ParameterObject.hasText(p.getEncoding()) || !ZxingValidation.checkEncoding(p.getEncoding().toLowerCase())) {
                p.setEncoding(StandardCharsets.ISO_8859_1.name());
            }
            if (p.getEcLevel() == null) {
                p.setEcLevel(ZxingUtil.parseErrorCorrectLevel("l"));
            }
            if (!ParameterObject.hasInteger(p.getMargin())) {
                p.setMargin(1);
            }
            if (!ParameterObject.hasInteger(p.getWidth()) || !ParameterObject.hasInteger(p.getHeight())) {
                p.setWidth(200);
                p.setHeight(200);
            }
//            if (p.getWidth() != p.getHeight()) {
//                p.setWidth(Math.max(p.getWidth(), p.getHeight()));
//                p.setHeight(Math.max(p.getWidth(), p.getHeight()));
//            }
            if (!ParameterObject.hasInteger(p.getBackgroundColor())) {
                p.setBackgroundColor(ZxingImage.WHITE);
            }
            if (!ParameterObject.hasInteger(p.getForegroundColor())) {
                p.setForegroundColor(ZxingImage.BLACK);
            }
            if (!ParameterObject.hasText(p.getFormat()) || !ZxingValidation.checkFormat(p.getFormat().toLowerCase())) {
                p.setFormat("png");
            }
            if (!ParameterObject.hasBoolean(p.isNeedBase64())) {
                p.setNeedBase64(false);
            }
            log.info(gson.toJson(p));
        } else {
            response.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, "Request error!!!");
            return;
        }

//        if(!ZxingUtil.checkData(p)) {
//            String errorMsg = "Date content OR parameter ERROR!";
//            log.error(errorMsg);
//            response.setContentType("text/plain");
//            response.getOutputStream().print(errorMsg);
//            return;
//        }

        if ("text".equals(p.getFormat())) {
            String outMsg = "";
            try {
                outMsg = ParameterObject.convertByteArrays(ZxingUtil.encode(p).getMatrix().getArray());
            } catch (IllegalArgumentException | WriterException ex) {
                outMsg = "Create Fail!!!";
                if (log.isDebugEnabled()) {
                    log.error(outMsg, ex);
                } else {
                    log.error(outMsg);
                }
            }
            response.setContentType("text/plain");
            response.getOutputStream().print(outMsg);
            return;
        }

        BufferedImage data = null;
        try {
            data = ZxingUtil.encodeWithImg(p);
        } catch (IllegalArgumentException | WriterException ex) {
            String errorMsg = "Create Fail!!!";
            if (log.isDebugEnabled()) {
                log.error(errorMsg, ex);
            } else {
                log.error(errorMsg);
            }
            response.setContentType("text/plain");
            response.getOutputStream().print(errorMsg);
            return;
        }

        ByteArrayOutputStream out = new ByteArrayOutputStream();
        if (!ImageIO.write(data, p.getFormat(), out)) {
            log.error("Could not write an image of format " + p.getFormat());
            throw new IOException("Could not write an image of format " + p.getFormat());
        }
        if (p.isNeedBase64()) {
            String base64 = Base64.encodeToString(out.toByteArray());
            response.setContentType("text/plain");
            response.getOutputStream().print(base64);
        } else {
            response.setContentType("image/" + p.getFormat());
            response.setContentLength(out.size());
            response.getOutputStream().write(out.toByteArray());
        }

    }
}
